package com.greyshark.lib.bluetooth

import android.Manifest
import android.app.Service
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCallback
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattDescriptor
import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothProfile
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Binder
import android.os.Build
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import androidx.core.app.ActivityCompat
import com.greyshark.lib.bluetooth.core.BleConst
import com.greyshark.lib.bluetooth.entity.ScanDeviceEntity
import com.greyshark.lib.bluetooth.extensions.toHexString
import com.greyshark.lib.bluetooth.parse.PacketParse
import com.greyshark.lib.bluetooth.scan.BaseBleScanner
import com.greyshark.lib.bluetooth.scan.MyBleScanState
import com.greyshark.lib.bluetooth.scan.NordicsemiBleScanner
import com.greyshark.lib.bluetooth.scan.SimpleScanCallback
import com.greyshark.lib.bluetooth.scan.scanner.ScanResult
import com.greyshark.lib.bluetooth.utils.CreateNotificationUtils
import com.greyshark.lib.bluetooth.utils.LogUtils
import com.greyshark.lib.bluetooth.utils.MusicUtils
import com.greyshark.lib.bluetooth.utils.TimeoutManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.ProducerScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withTimeoutOrNull
import java.util.Collections
import java.util.Locale
import java.util.UUID
import kotlin.math.abs
import kotlin.math.min

const val KEY_CONNECT_COUNTDOWN = "CONNECT_COUNTDOWN"
const val KEY_CONNECT_TIMEOUT = "KEY_CONNECT_TIMEOUT"
const val KEY_DISCOVER_SERVICE_TIMEOUT = "KEY_DISCOVER_SERVICE_TIMEOUT"
const val KEY_NOTIFY_TIMEOUT = "KEY_NOTIFY_TIMEOUT"

class BluetoothKtService : Service() {

  private val tag: String = BluetoothKtService::class.java.simpleName

  var isConnect: Boolean = false

  var isConnecting: Boolean = false

  var bindAddress: String? = ""

  var bindName: String? = ""

  private var isSendOver = false

  private var isSendCmd = false

  private var timeoutCount = 0

  private var currentConnectState = ConnectStatusEnum.BLE_STATUS_DISCONNECTED

  private var connectTimeoutCount = 0

  private var scanner: BaseBleScanner? = null

  private val mainScope = CoroutineScope(Dispatchers.Main)

  private val ioScope = CoroutineScope(Dispatchers.IO + Job())

  private val commandQueue = ArrayDeque<ByteArray>()

  private val mutex = Mutex()
  private val sendMutex = Mutex()

  private var bluetoothManager: BluetoothManager? = null
  private var bluetoothAdapter: BluetoothAdapter? = null
  private var bluetoothGatt: BluetoothGatt? = null

  //上下文
  private lateinit var context: Context

  //指令缓存集合
  private val packetList = Collections.synchronizedList(ArrayList<ByteArray>())

  private val binder = BluetoothBinder()

  //region BindService
  inner class BluetoothBinder : Binder() {
    val service: BluetoothKtService
      get() = this@BluetoothKtService
  }

  override fun onBind(intent: Intent?): IBinder? {
    return binder
  }

  override fun onCreate() {
    super.onCreate()
    context = this

    stopScanHandler = Handler(Looper.getMainLooper())

    scanner = NordicsemiBleScanner(this, simpleScanCallback)
    bluetoothManager = getSystemService(BLUETOOTH_SERVICE) as BluetoothManager
    bluetoothAdapter = bluetoothManager?.adapter
    init()

    CreateNotificationUtils.instance.startForeground(
      service = this,
      notificationTitle = ControlCenterClient.instance.notificationTitle,
      notificationText = ControlCenterClient.instance.notificationBody,
      notifyAppIcon = ControlCenterClient.instance.notificationAppIconRes
    )
    MusicUtils.instance.playMusic(
      context = this
    )

    processCmdThread = Thread(processCmdRunnable)
    processCmdThread.start()
    processCmd = true
    isSendOver = false
  }

  /**
   * 初始化相关参数
   */
  private fun initParams() {
    //发送线程
    packetList.clear()
    isSendOver = true
    isSendCmd = false
    sendStartTime = 0
    sendingData = byteArrayOf()
  }

  //endregion
  //region 扫描
  /**
   * 蓝牙扫描回调
   */
  private var simpleScanCallback = object : SimpleScanCallback {
    var scope: ProducerScope<ScanDeviceEntity>? = null

    override fun onBleScan(
      device: BluetoothDevice?,
      rssi: Int,
      scanRecord: ByteArray?
    ) {
    }

    override fun onBleScan(results: MutableList<ScanResult>?) {
      results?.let {
        LogUtils.d(tag, "scanDevice onBleScan size = ${it.size}")
        for (item in it) {
          val device = ScanDeviceEntity(
            device = item.device,
            name = item.device?.name.toString(),
            address = item.device?.address.toString(),
            rssi = item.rssi.toString()
          )
          scope?.trySend(device)
          LogUtils.d(tag, "scan device name = ${device.name}, address:${device.address}")
        }
      } ?: run {
        LogUtils.d(tag, "scanDevice onBleScan results = null")
      }
    }

    override fun onBleScanStop(scanState: MyBleScanState?) {
      LogUtils.e(tag, "onBleScanStop exception info : ${scanState?.message}")
      scope?.close(Exception("扫描已停止,状态为: $scanState"))
    }
  }

  private lateinit var stopScanHandler: Handler

  private var scanTime = 0L

  private val stopScanDeviceRunnable: Runnable = Runnable {
    LogUtils.d(tag, "stopScanDeviceRunnable ")
    stopScan()
  }

  fun startScan(timeout: Long): Flow<ScanDeviceEntity> = callbackFlow {
    LogUtils.w(tag, "start scan device")
    simpleScanCallback.scope = this
    if (scanner == null) {
      scanner = NordicsemiBleScanner(this@BluetoothKtService, simpleScanCallback)
    }
    scanner?.apply {
      if (isScanning) {
        close(Exception("已经在扫描中"))
      }
      scanTime = System.currentTimeMillis()
      onStopBleScan()
      onStartBleScan()
      stopScanHandler.removeCallbacksAndMessages(null)
      stopScanHandler.postDelayed(
        stopScanDeviceRunnable,
        BleConst.SCAN_PERIOD_BLE_SERVICE
      )
    }
    awaitClose {
      scanner?.onStopBleScan()
      stopScanHandler.removeCallbacksAndMessages(null)
      simpleScanCallback.scope = null // 清理 scope
      LogUtils.w(tag, "Flow closed, scan stopped")
    }
  }

  /**
   * 停止扫描
   */
  fun stopScan() {
    LogUtils.w(tag, "stop scan device")
    if (scanTime != 0L) {
      scanTime = System.currentTimeMillis() - scanTime
      LogUtils.d(tag, "Bluetooth scan time : $scanTime")
    }
    scanTime = 0
    if (scanner == null) {
      scanner = NordicsemiBleScanner(this, simpleScanCallback)
    }
    scanner?.onStopBleScan()
    stopScanHandler.removeCallbacksAndMessages(null)
  }

  //endregion
  //region 处理连接设备，断连设备

  /**
   * 初始化蓝牙相关
   *
   * @return
   */
  private fun init(): Boolean {
    try {
      if (bluetoothManager == null) {
        ControlCenterClient.instance._connectEventFlow.tryEmit(
          BleConnectEvent.ConnectParamIllegalEvent(
            "Unable to init BluetoothManager"
          )
        )
        LogUtils.d(tag, "Unable to initialize BluetoothManager")
        return false
      }
      if (bluetoothAdapter == null) {
        ControlCenterClient.instance._connectEventFlow.tryEmit(
          BleConnectEvent.ConnectParamIllegalEvent(
            "Unable to init a BluetoothAdapter"
          )
        )
        LogUtils.d(tag, "Unable to initialize a BluetoothAdapter")
        return false
      }
      return true
    } catch (e: Exception) {
      ControlCenterClient.instance._connectEventFlow.tryEmit(
        BleConnectEvent.ConnectUnknowErrorEvent(e)
      )
      LogUtils.e(tag, "initialize exception :${e.message}")
    }
    return false
  }

  /**
   * 连接设备
   *
   * @param name
   * @param address
   */
  fun connect(name: String?, address: String?) {
    if (name.isNullOrEmpty() || address.isNullOrEmpty()) {
      ControlCenterClient.instance._connectEventFlow.tryEmit(
        BleConnectEvent.ConnectParamIllegalEvent("connect bluetooth device name or mac is empty")
      )
      return
    }
    realConnect(name, address)
  }

  /**
   * 连接设备
   *
   * @param name
   * @param address
   */
  private fun realConnect(name: String, address: String) {
    LogUtils.e(tag, "connect name = $name; address = $address; isConnecting $isConnecting")
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
      if (ActivityCompat.checkSelfPermission(
          context,
          Manifest.permission.BLUETOOTH_CONNECT
        ) != PackageManager.PERMISSION_GRANTED
      ) {
        ControlCenterClient.instance._connectEventFlow.tryEmit(
          BleConnectEvent.PermissionDeniedEvent("bluetooth connect permission denied")
        )
        LogUtils.i(tag, "${Manifest.permission.BLUETOOTH_CONNECT} denied")
        return
      }
      if (ActivityCompat.checkSelfPermission(
          context,
          Manifest.permission.BLUETOOTH_SCAN
        ) != PackageManager.PERMISSION_GRANTED
      ) {
        ControlCenterClient.instance._connectEventFlow.tryEmit(
          BleConnectEvent.PermissionDeniedEvent("bluetooth scan permission denied")
        )
        LogUtils.i(tag, "${Manifest.permission.BLUETOOTH_SCAN} denied")
        return
      }
    }

    synchronized(this@BluetoothKtService) {
      if (isConnecting) {
        if (bindName == name && bindAddress == address) {
          LogUtils.d(tag, "connect cancel, already connecting")
          return
        }
      }
      bindName = name
      bindAddress = address.uppercase(Locale.getDefault())
      disconnect()

      try {
        val device = bluetoothAdapter?.getRemoteDevice(address.uppercase(Locale.getDefault()))
        if (device == null) {
          LogUtils.e(tag, "connect remote device == null")
          ControlCenterClient.instance._connectEventFlow.tryEmit(
            BleConnectEvent.ConnectDeviceNotFoundEvent("remote device is null")
          )
          isConnecting = false
          connectRxError()
          return
        }
        isConnecting = true
        ControlCenterClient.instance._connectEventFlow.tryEmit(
          BleConnectEvent.ConnectingEvent("bluetooth scan permission denied")
        )

        TimeoutManager.instance.startTimeout(KEY_CONNECT_COUNTDOWN, BleConst.DELAYED_CONNECT_TIME) {
          connectTimeoutCount++
          if (connectTimeoutCount > BleConst.CONNECT_TIMES) {
            connectTimeoutCount = 0
            TimeoutManager.instance.cancelTimeout(KEY_CONNECT_TIMEOUT)
            connectRxError()
            ControlCenterClient.instance._connectEventFlow.tryEmit(
              BleConnectEvent.ConnectTimeOutEvent(
                info = "connect device count limit, emit timeout event"
              )
            )
            return@startTimeout
          }

          TimeoutManager.instance.startTimeout(KEY_CONNECT_TIMEOUT, BleConst.CONNECT_TIMEOUT) {
            LogUtils.d(tag, "start connect timeout task")
            if (currentConnectState == ConnectStatusEnum.BLE_STATUS_CONNECTED) {
              LogUtils.i(tag, "connect timeout task has end, but connect state = connected")
              return@startTimeout
            }
            ControlCenterClient.instance._connectEventFlow.tryEmit(
              BleConnectEvent.SingleConnectTimeOutEvent(
                info = "connect device count limit, emit timeout event"
              )
            )
            reconnect(bindName, bindAddress)
          }

          bluetoothGatt = when {
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> {
              device.connectGatt(
                context, false, gattCallback,
                BluetoothDevice.TRANSPORT_LE, BluetoothDevice.PHY_LE_1M_MASK
              )
            }

            Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> {
              device.connectGatt(
                context, false, gattCallback,
                BluetoothDevice.TRANSPORT_LE
              )
            }

            else -> {
              device.connectGatt(context, false, gattCallback)
            }
          }

          LogUtils.d(
            tag, ("${Thread.currentThread().name}," +
                "connect device name = ${device.getName()}, " +
                "Address:${device.getAddress()}, " +
                "BondState:${device.getBondState()}, " +
                "Type:${device.getType()},$bluetoothGatt")
          )
        }
      } catch (e: Exception) {
        //连接异常
        LogUtils.e(tag, "connect device exception :${e.message}")
        connectRxError()
        ControlCenterClient.instance._connectEventFlow.tryEmit(
          BleConnectEvent.ConnectUnknowErrorEvent(e)
        )
      }
    }
  }

  fun reconnect(name: String?, address: String?) {
    if (name.isNullOrEmpty() || address.isNullOrEmpty()) {
      ControlCenterClient.instance._connectEventFlow.tryEmit(
        BleConnectEvent.ConnectParamIllegalEvent("reconnect bluetooth device name or mac is empty")
      )
      return
    }
    bindName = name
    bindAddress = address
    isConnecting = false
    connect(bindName, bindAddress)
    LogUtils.w(tag, "reconnectDevice name=$name address=$address")
  }

  fun disconnect(isUser: Boolean = false) {
    LogUtils.d(tag, "disconnect")
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
      if (ActivityCompat.checkSelfPermission(
          context,
          Manifest.permission.BLUETOOTH_CONNECT
        ) != PackageManager.PERMISSION_GRANTED
      ) {
        ControlCenterClient.instance._connectEventFlow.tryEmit(
          BleConnectEvent.PermissionDeniedEvent("bluetooth connect permission denied")
        )
        LogUtils.i(tag, "${Manifest.permission.BLUETOOTH_CONNECT} denied")
        return
      }
      if (ActivityCompat.checkSelfPermission(
          context,
          Manifest.permission.BLUETOOTH_SCAN
        ) != PackageManager.PERMISSION_GRANTED
      ) {
        ControlCenterClient.instance._connectEventFlow.tryEmit(
          BleConnectEvent.PermissionDeniedEvent("bluetooth scan permission denied")
        )
        LogUtils.i(tag, "${Manifest.permission.BLUETOOTH_SCAN} denied")
        return
      }
    }
    try {
      initParams()
      TimeoutManager.instance.cancelTimeout(KEY_CONNECT_COUNTDOWN)
      TimeoutManager.instance.cancelTimeout(KEY_CONNECT_TIMEOUT)
      TimeoutManager.instance.cancelTimeout(KEY_DISCOVER_SERVICE_TIMEOUT)
      TimeoutManager.instance.cancelTimeout(KEY_NOTIFY_TIMEOUT)

      bluetoothGatt?.apply {
        close()
        LogUtils.d(tag, "disconnect device bluetoothGatt == $bluetoothGatt")
      } ?: run {
        LogUtils.d(tag, "disconnect device bluetoothGatt == null")
      }
      isConnect = false
      isConnecting = false
      bluetoothGatt = null
      if (isUser) {
        bindAddress = ""
        bindName = ""
      }

      if (currentConnectState == ConnectStatusEnum.BLE_STATUS_CONNECTED) {
        ControlCenterClient.instance._connectEventFlow.tryEmit(
          BleConnectEvent.DisConnectedEvent("")
        )
      }
      currentConnectState = ConnectStatusEnum.BLE_STATUS_DISCONNECTED
    } catch (e: Exception) {
      LogUtils.e(tag, "disconnect exception :${e.message}")
      ControlCenterClient.instance._connectEventFlow.tryEmit(
        BleConnectEvent.ConnectUnknowErrorEvent(e)
      )
    }
  }

  private fun connectRxError() {
    LogUtils.e(tag, "connectRxError")
    mainScope.launch {
      mutex.withLock {
        TimeoutManager.instance.cancelTimeout(KEY_CONNECT_TIMEOUT)
        TimeoutManager.instance.startTimeout(
          KEY_CONNECT_TIMEOUT,
          BleConst.CONNECT_RX_ERROR_DELAYED_TIME
        ) {
          disconnect()
          reconnect(bindName, bindAddress)
        }
      }
    }
  }

  //endregion

  private var discoverServicesTime = 0L

  private val gattCallback = object : BluetoothGattCallback() {

    override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
      super.onConnectionStateChange(gatt, status, newState)
      LogUtils.e(tag, "onConnectionStateChange status = $status newState =$newState")
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        if (ActivityCompat.checkSelfPermission(
            context,
            Manifest.permission.BLUETOOTH_CONNECT
          ) != PackageManager.PERMISSION_GRANTED
        ) {
          ControlCenterClient.instance._connectEventFlow.tryEmit(
            BleConnectEvent.PermissionDeniedEvent("bluetooth connect permission denied")
          )
          LogUtils.i(tag, "${Manifest.permission.BLUETOOTH_CONNECT} denied")
          return
        }
        if (ActivityCompat.checkSelfPermission(
            context,
            Manifest.permission.BLUETOOTH_SCAN
          ) != PackageManager.PERMISSION_GRANTED
        ) {
          ControlCenterClient.instance._connectEventFlow.tryEmit(
            BleConnectEvent.PermissionDeniedEvent("bluetooth scan permission denied")
          )
          LogUtils.i(tag, "${Manifest.permission.BLUETOOTH_SCAN} denied")
          return
        }
      }

      if (bluetoothGatt != null) {
        LogUtils.e(tag, "onConnectionStateChange1 bluetoothGatt:$gatt")
        if (bluetoothGatt != gatt) {
          if (gatt != null) {
            gatt.close()
            LogUtils.e(tag, "onConnectionStateChange1 gatt:$gatt")
          } else {
            LogUtils.e(tag, "onConnectionStateChange1 gatt == null")
          }
          ControlCenterClient.instance._connectEventFlow.tryEmit(
            BleConnectEvent.ConnectDeviceGattErrorEvent("connect device gatt has old object")
          )
        }
      } else {
        LogUtils.e(tag, "onConnectionStateChange2 bluetoothGatt == null")
        if (gatt != null) {
          gatt.close()
          LogUtils.e(tag, "onConnectionStateChange2 gatt:$gatt")
        } else {
          LogUtils.e(tag, "onConnectionStateChange2 gatt == null")
        }
        return
      }
      if (status == 133 || status == 255 || status == 257) {
        ControlCenterClient.instance._connectEventFlow.tryEmit(
          BleConnectEvent.ConnectDeviceGattErrorEvent("status error code is $status")
        )
        connectRxError()
        return
      }
      TimeoutManager.instance.cancelTimeout(KEY_CONNECT_TIMEOUT)
      when (newState) {
        BluetoothProfile.STATE_CONNECTED -> {
          LogUtils.d(tag, "onConnectionStateChange device is connected")
          isConnect = true
          isConnecting = false
          isSendOver = false
          currentConnectState = ConnectStatusEnum.BLE_STATUS_CONNECTED
          connectTimeoutCount = 0

          bluetoothGatt?.device?.let {
            if (it.getAddress() != BleConst.CHANGE_GATT_MAC
              && it.getName()?.isNotEmpty() == true
              && bindName == it.getName()
            ) {
              bindName = it.getName()
            }
          }

          TimeoutManager.instance.startTimeout(
            KEY_DISCOVER_SERVICE_TIMEOUT,
            BleConst.DISCOVER_SERVICES_TIMEOUT
          ) {
            LogUtils.e(tag, "discoverServices timeout error")
            connectRxError()
            ControlCenterClient.instance._connectEventFlow.tryEmit(
              BleConnectEvent.ConnectDiscoverServiceTimeOutEvent("discoverServices timeout")
            )
          }

          discoverServicesTime = System.currentTimeMillis()
          val isSuccess = bluetoothGatt?.discoverServices()
          LogUtils.w(tag, "onConnectionStateChange discoverServices = $isSuccess")
        }

        BluetoothProfile.STATE_DISCONNECTED -> {
          disconnect()
          when (bluetoothAdapter?.state) {
            BluetoothAdapter.STATE_OFF -> {
              LogUtils.w(tag, "onConnectionStateChange BluetoothAdapter.off")
            }

            BluetoothAdapter.STATE_ON -> {
              LogUtils.w(tag, "onConnectionStateChange BluetoothAdapter.on")
              TimeoutManager.instance.startTimeout(
                KEY_CONNECT_COUNTDOWN,
                BleConst.DELAYED_CONNECT_TIME
              ) {
                LogUtils.w(tag, "bind name = $bindName; bind address = $bindAddress")
                if (bindName.isNullOrEmpty() || bindAddress.isNullOrEmpty()) {
                  return@startTimeout
                }
                isConnecting = false
                connectTimeoutCount = 0
                connect(bindName, bindAddress)
              }
            }

            else -> {
              LogUtils.w(
                tag,
                "onConnectionStateChange BluetoothAdapter.STATE is ${bluetoothAdapter?.state}"
              )
            }
          }
        }

        else -> {
          LogUtils.e(tag, "connect other state = $newState")
          disconnect()
        }
      }
    }

    override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) {
      super.onServicesDiscovered(gatt, status)
      TimeoutManager.instance.cancelTimeout(KEY_DISCOVER_SERVICE_TIMEOUT)
      discoverServicesTime = System.currentTimeMillis() - discoverServicesTime
      LogUtils.i(tag, "onServicesDiscovered time : $discoverServicesTime")

      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        if (ActivityCompat.checkSelfPermission(
            context,
            Manifest.permission.BLUETOOTH_CONNECT
          ) != PackageManager.PERMISSION_GRANTED
        ) {
          ControlCenterClient.instance._connectEventFlow.tryEmit(
            BleConnectEvent.PermissionDeniedEvent("bluetooth connect permission denied")
          )
          LogUtils.i(tag, "${Manifest.permission.BLUETOOTH_CONNECT} denied")
          return
        }
        if (ActivityCompat.checkSelfPermission(
            context,
            Manifest.permission.BLUETOOTH_SCAN
          ) != PackageManager.PERMISSION_GRANTED
        ) {
          ControlCenterClient.instance._connectEventFlow.tryEmit(
            BleConnectEvent.PermissionDeniedEvent("bluetooth scan permission denied")
          )
          LogUtils.i(tag, "${Manifest.permission.BLUETOOTH_SCAN} denied")
          return
        }
      }

      LogUtils.i(tag, "onServicesDiscovered success state is 0 and now status = $status")

      when (status) {
        BluetoothGatt.GATT_SUCCESS -> {
          ioScope.launch {
            mutex.withLock {
              try {
                val gattServices = bluetoothGatt?.getServices()
                if (gattServices == null || gattServices.isEmpty()) {
                  LogUtils.i(tag, "onServicesDiscovered gattServices isEmpty")
                  connectRxError()
                  return@launch
                }

                for (gattService in gattServices) {
                  if (gattService == null) {
                    continue
                  }
                  if (gattService.uuid != null) {
                    LogUtils.i(tag, "discovered service uuid = ${gattService.uuid}")
                    if (gattService.uuid.toString().contains("56ff", ignoreCase = true)) {
                      for (characteristic in gattService.characteristics) {
                        LogUtils.i(tag, "characteristic uuid = " + characteristic.uuid.toString())
                        if (characteristic.uuid.toString().contains("33f3", ignoreCase = true)
                          || characteristic.uuid.toString().contains("33f4", ignoreCase = true)
                        ) {
                          channelList.add(characteristic)
                        }
                      }
                    }
                  }
                }

                LogUtils.d(tag, "channel total size = ${channelList.size}")
                if (channelList.isEmpty()) {
                  LogUtils.e(tag, "channel size is empty")
//                  refreshGattCache(gatt)
                  connectRxError()
                  return@launch
                }

                for (channel in channelList) {
                  if (channel.uuid.toString().contains("33f4", ignoreCase = true)) {
                    val result = enableGattNotification(bluetoothGatt, channel)
                    LogUtils.e(tag, "enableGattNotification result is $result")
//                  if (!result) {
//                    connectRxError()
//                    return@launch
//                  }
//                  Thread.sleep(2)
                  }
                }

                initParams()
                mainScope.launch {
                  mutex.withLock {
                    isConnecting = false
                    ControlCenterClient.instance._connectEventFlow.tryEmit(
                      BleConnectEvent.ConnectedEvent("")
                    )
                    sendCommand(ProtocolCommand.queryMTU())
                  }
                }
              } catch (e: Exception) {
                LogUtils.i(tag, "onServicesDiscovered is Exception = $e")
                ControlCenterClient.instance._connectEventFlow.tryEmit(
                  BleConnectEvent.ConnectUnknowErrorEvent(
                    e
                  )
                )
                connectRxError()
              }
            }
          }
        }

        else -> {
          ControlCenterClient.instance._connectEventFlow.tryEmit(
            BleConnectEvent.ConnectDeviceGattErrorEvent("onServicesDiscovered gatt status is failure")
          )
          connectRxError()
        }
      }
    }

    override fun onDescriptorWrite(
      gatt: BluetoothGatt?,
      descriptor: BluetoothGattDescriptor?,
      status: Int
    ) {
      super.onDescriptorWrite(gatt, descriptor, status)
      if (status == BluetoothGatt.GATT_SUCCESS) {
        LogUtils.d(tag, "onDescriptorWrite status = GATT_SUCCESS")
      } else {
        connectRxError()
        LogUtils.d(tag, "onDescriptorWrite status = GATT_FAILURE")
      }
    }

    override fun onCharacteristicChanged(
      gatt: BluetoothGatt?,
      characteristic: BluetoothGattCharacteristic
    ) {
      super.onCharacteristicChanged(gatt, characteristic)
      characteristic.value?.apply {
        LogUtils.i(
          tag, "onCharacteristicChanged packet = ${this.toHexString()}"
        )
      }
      PacketParse.instance.parsePacket(characteristic.value)
    }

    override fun onMtuChanged(gatt: BluetoothGatt?, mtu: Int, status: Int) {
      super.onMtuChanged(gatt, mtu, status)
      LogUtils.i(tag, "mtu size = $mtu")
    }
  }

  fun chanceMTU(size: Int) {
    Handler(mainLooper).postDelayed({
      bluetoothGatt?.requestMtu(size)
    }, 1000)
  }
  //endregion

  private var enableTime: Long = 0
  private val channelList = arrayListOf<BluetoothGattCharacteristic>()

  private fun enableGattNotification(
    gatt: BluetoothGatt?,
    characteristic: BluetoothGattCharacteristic
  ): Boolean {
    enableTime = System.currentTimeMillis()

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
      if (ActivityCompat.checkSelfPermission(
          context,
          Manifest.permission.BLUETOOTH_CONNECT
        ) != PackageManager.PERMISSION_GRANTED
      ) {
        ControlCenterClient.instance._connectEventFlow.tryEmit(
          BleConnectEvent.PermissionDeniedEvent("bluetooth connect permission denied")
        )
        LogUtils.i(tag, "${Manifest.permission.BLUETOOTH_CONNECT} denied")
        return false
      }
      if (ActivityCompat.checkSelfPermission(
          context,
          Manifest.permission.BLUETOOTH_SCAN
        ) != PackageManager.PERMISSION_GRANTED
      ) {
        ControlCenterClient.instance._connectEventFlow.tryEmit(
          BleConnectEvent.PermissionDeniedEvent("bluetooth scan permission denied")
        )
        LogUtils.i(tag, "${Manifest.permission.BLUETOOTH_SCAN} denied")
        return false
      }
    }

    if (gatt == null) {
      LogUtils.i(tag, "enable device notify gatt is null")
      ControlCenterClient.instance._connectEventFlow.tryEmit(
        BleConnectEvent.ConnectDeviceGattErrorEvent("enable device notify gatt is null")
      )
      return false
    }

    try {
      LogUtils.i(tag, "enable gatt notification uuid = ${characteristic.uuid}")
      gatt.setCharacteristicNotification(characteristic, true)
      val descriptor = characteristic.getDescriptor(
        BleConst.UUID_CHARACTERISTIC_UPDATE_NOTIFICATION_DESCRIPTOR
      )
      descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)

//      notifyTimeoutTask.start(BleConst.ENABLE_CHARACTERISTIC_TIMEOUT) {
//        LogUtils.w(tag, "enableGattNotification channel timeout")
//        isEnableNotificationTimeout = true
//        connectRxError()
//      }
      return gatt.writeDescriptor(descriptor)
    } catch (e: Exception) {
      LogUtils.e(tag, "enableGattNotification exception info: $e")
      ControlCenterClient.instance._connectEventFlow.tryEmit(
        BleConnectEvent.ConnectUnknowErrorEvent(e)
      )
      return false
    }
  }
  //endregion

  //region 指令发送线程
  private lateinit var processCmdThread: Thread
  private var processCmd = true
  private var sendingData: ByteArray = byteArrayOf()
  private var sendStartTime: Long = 0

  private var isProcessing = false
  private var currentCommand: ByteArray? = null
  private var retryCount = 0
  private val maxRetries: Int = 1

  // 添加一个对象用于线程间同步
  private val cmdLock = Any()

  var processCmdRunnable = object : Runnable {
    override fun run() {
      // 使用自适应的睡眠时间，根据任务负载动态调整
      val baseSleepTime = BleConst.CMD_THREAD_SLEEP_TIME
      val maxSleepTime = baseSleepTime * 100 // 最大睡眠时间
      var currentSleepTime = baseSleepTime
      while (processCmd) {
        try {
          // 使用带超时的等待，而不是固定的sleep
          // 这样可以在有新任务时立即唤醒，或者在超时后继续执行检查逻辑
          synchronized(cmdLock) {
            (cmdLock as Object).wait(currentSleepTime)
          }
          if (currentConnectState == ConnectStatusEnum.BLE_STATUS_CONNECTED) {
            if (isSendOver) {
              if (packetList.isNotEmpty()) {
                // 有任务时，重置睡眠时间
                currentSleepTime = baseSleepTime
                LogUtils.d(tag, "CMDThread send size = " + packetList.size)
                sendingData = packetList[0]!!
                isSendOver = false
                isSendCmd = false
                sendStartTime = System.currentTimeMillis()
              } else {
                // 没有任务时，逐渐增加睡眠时间以节省电量
                currentSleepTime = min(currentSleepTime * 10, maxSleepTime)
              }
            } else {
              if (sendStartTime != 0L && abs(System.currentTimeMillis() - sendStartTime) > BleConst.SINGLE_CMD_TIME_OUT) {
                timeoutCount++
                LogUtils.e(
                  tag,
                  "process_cmd_runnable send cmd time out : timeoutNum = $timeoutCount，cmdList = ${packetList.size}, service:$this"
                )

                //指令发送超时失败，尝试补发一次
                //(设置时间的指令超时后不补发,时间过期,直接丢弃)//已经超时补发过一次了
                if (timeoutCount > 1) {
                  if (packetList.isNotEmpty()) {
                    if (sendingData.contentEquals(packetList[0])) {
                      packetList.removeAt(0)
                    }
                  }
                  sendingData = byteArrayOf()
                  timeoutCount = 0
                }
                isSendOver = true
              }
            }
          }
        } catch (e: Exception) {
          LogUtils.e(tag, "process_cmd_runnable exception: $e")
          ControlCenterClient.instance._connectEventFlow.tryEmit(
            BleConnectEvent.ConnectUnknowErrorEvent(
              e
            )
          )
        }
      }
    }
  }

  // 添加指令到队列（可从UI线程调用）
  fun enqueueCommand(command: ByteArray) {
    ioScope.launch {
      sendMutex.withLock {
        commandQueue.addLast(command)
        LogUtils.i(tag, "add command")
        if (!isProcessing) {
          isProcessing = true
          processQueue()
        }
      }
    }
  }

  // 处理队列的协程循环
  private suspend fun processQueue() {
    while (true) {
      if (commandQueue.isEmpty()) {
        isProcessing = false
        return
      }
      val command = commandQueue.removeFirst()

      currentCommand = command
      retryCount = 0
      var success = false
      LogUtils.i(tag, "process queue")

      while (retryCount < maxRetries && !success) {
        success = sendAndWaitForResponse(command)
        LogUtils.w(tag, "sendAndWaitForResponse is $success")
        if (!success) {
          retryCount++
          LogUtils.i(tag, "重试第 $retryCount 次：${command.toHexString()}")
        }
      }
      LogUtils.w(tag, "sendAndWaitForResponse is over")
      if (!success) {
        LogUtils.i(tag, "命令发送失败：${command.toHexString()}")
      }
    }
  }

  private suspend fun sendAndWaitForResponse(command: ByteArray): Boolean {
    sendCommand(command) // 实际发送
    LogUtils.i(tag, "发送命令: ${command.toHexString()}")
    val commandType = PacketParse.instance.readLittleEndianInt(command, 2)
    return withTimeoutOrNull(5_000L) {
      PacketParse.instance.responseFlow.firstOrNull { type ->
        LogUtils.i(tag, "response type: $type, command type: $commandType")
        type == commandType
      } != null
    } ?: false
  }

  // 等待响应完成（使用通道或超时）
  private suspend fun waitForResponse(command: ByteArray) {
//    // 等待通道信号或超时（10秒）
//    val completed = withTimeoutOrNull(10000) {
//      // 阻塞直到收到true（响应完成，即当前包序号 == 总包数）
//      LogUtils.i(tag, "responseChannel receive")
//      PacketParse.instance.responseChannel.receive()
//    }
//    if (completed == true) {
//      LogUtils.i(tag, "Response completed")
//      // 结束当前阻塞，继续处理队列（如果有新指令，会开启新阻塞）
//    } else {
//      // 超时：结束当前阻塞，重发当前指令
//      LogUtils.e(tag, "Response timeout after 10s, re-enqueue command")
//      enqueueCommand(command)  // 重发到队列
//      // 不继续等待，直接返回，继续处理下一个（如果有）
//    }
  }
  //endregion

  //region app数据发送至设备
  fun sendCommand(
    value: ByteArray,
    serviceUUID: UUID = UUID.fromString("000056ff-0000-1000-8000-00805f9b34fb"),
    channelUUID: UUID = UUID.fromString("000033f3-0000-1000-8000-00805f9b34fb")
  ) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
      if (ActivityCompat.checkSelfPermission(
          context,
          Manifest.permission.BLUETOOTH_CONNECT
        ) != PackageManager.PERMISSION_GRANTED
      ) {
        ControlCenterClient.instance._connectEventFlow.tryEmit(
          BleConnectEvent.PermissionDeniedEvent("bluetooth connect permission denied")
        )
        LogUtils.i(tag, "${Manifest.permission.BLUETOOTH_CONNECT} denied")
        return
      }
      if (ActivityCompat.checkSelfPermission(
          context,
          Manifest.permission.BLUETOOTH_SCAN
        ) != PackageManager.PERMISSION_GRANTED
      ) {
        ControlCenterClient.instance._connectEventFlow.tryEmit(
          BleConnectEvent.PermissionDeniedEvent("bluetooth scan permission denied")
        )
        LogUtils.i(tag, "${Manifest.permission.BLUETOOTH_SCAN} denied")
        return
      }
    }

    try {
      if (bluetoothGatt == null) {
        ControlCenterClient.instance._connectEventFlow.tryEmit(
          BleConnectEvent.ConnectDeviceGattErrorEvent("connect device gatt is null")
        )
        return
      }
      val gattWriteService = bluetoothGatt?.getService(serviceUUID)
      if (gattWriteService == null) {
        ControlCenterClient.instance._connectEventFlow.tryEmit(
          BleConnectEvent.ConnectDeviceGattErrorEvent("connect device write service is null")
        )
        return
      }
      val gattWriteChannel = gattWriteService.getCharacteristic(channelUUID)
      if (gattWriteChannel == null) {
        ControlCenterClient.instance._connectEventFlow.tryEmit(
          BleConnectEvent.ConnectDeviceGattErrorEvent("connect device write channel is null")
        )
        return
      }
      if (ControlCenterClient.instance.packetDelayTime > 0 && ControlCenterClient.instance.packetDelayTime <= 200) {
        Thread.sleep(ControlCenterClient.instance.packetDelayTime)
      }

      gattWriteChannel.setValue(value)
      var status = bluetoothGatt?.writeCharacteristic(gattWriteChannel)

      var failureCount = 0
      for (i in 1..100) {
        if (status == true) {
          break
        }
        failureCount = i
        Thread.sleep(i.toLong())
        status = bluetoothGatt?.writeCharacteristic(gattWriteChannel)
      }
      LogUtils.i(
        tag, "write command = ${value.toHexString()}; "
            + "status = $status; "
            + "failureCount = $failureCount"
      )
      if (failureCount == 100) {
        if (value.size == 6
          && value[0].toInt() == 0
          && value[1].toInt() == 0
          && value[2].toInt() == 0
          && value[3].toInt() == 0
          && value[4].toInt() != 0
          && value[5].toInt() == 0
        ) {
          timeoutCount = 0
        }
      }
    } catch (e: Exception) {
      LogUtils.e(tag, "writeCharacteristic exception info:$e")
      ControlCenterClient.instance._connectEventFlow.tryEmit(
        BleConnectEvent.ConnectUnknowErrorEvent(e)
      )
    }
  }

  override fun onDestroy() {
    try {
      disconnect()
      scanner?.onStopBleScan()
      scanner = null

      TimeoutManager.instance.cancelTimeout(KEY_CONNECT_COUNTDOWN)
      TimeoutManager.instance.cancelTimeout(KEY_CONNECT_TIMEOUT)
      TimeoutManager.instance.cancelTimeout(KEY_DISCOVER_SERVICE_TIMEOUT)
      TimeoutManager.instance.cancelTimeout(KEY_NOTIFY_TIMEOUT)

      processCmd = false
      processCmdThread.interrupt()

      MusicUtils.instance.reset()
      CreateNotificationUtils.instance.removeNotification(this)
    } catch (e: Exception) {
      LogUtils.i(tag, "release Exception :${e.message}")
    }
    super.onDestroy()
  }

  internal fun release() {
    disconnect()
  }
}